package by.siarhei.java.tij.polymorphism;

import java.util.concurrent.TimeUnit;

/**
 * @author Siarhei Shchahratsou <s.siarhei@gmail.com>
 * @since 23.03.2014
 */
public class ReferenceCounting {
    private final Shared shared;
    private final Composing[] composing;
    private boolean disposeAllow;

    public ReferenceCounting() {
        this.shared = new Shared();
        this.composing = new Composing[] { new Composing(shared),
                new Composing(shared), new Composing(shared),
                new Composing(shared), new Composing(shared) };
    }

    @Override
    protected void finalize() throws Throwable {
        if (disposeAllow) {
            for (Composing c : composing) {
                c.dispose();
            }
            super.finalize();
        } else {
            System.out.println("Can't be disposed");
        }
    }

    public static void main(String[] args) {
        //new ReferenceCounting();  //Can't be disposed
        new ReferenceCounting().setDispose(true);
        System.gc();

        try {
            Thread.sleep(TimeUnit.SECONDS.toMillis(2));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    protected boolean canDispose() {
        return disposeAllow;
    }

    protected void setDispose(boolean disposeAllow) {
        this.disposeAllow = disposeAllow;
    }
}
class Shared {
    private int refcount = 0;
    private static long counter = 0;
    private final long id = counter++;
    public Shared() {
        System.out.println("Creating " + this);
    }
    public void addRef() { refcount++; }
    protected void dispose() {
        if(--refcount == 0)
            System.out.println("Disposing " + this);
    }
    public String toString() { return "Shared " + id; }
}

class Composing {
    private Shared shared;
    private static long counter = 0;
    private final long id = counter++;
    public Composing(Shared shared) {
        System.out.println("Creating " + this);
        this.shared = shared;
        this.shared.addRef();
    }
    protected void dispose() {
        System.out.println("disposing " + this);
        shared.dispose();
    }
    public String toString() { return "Composing " + id; }
}
